package ru.spb.nicetu.altx.mnemo.node;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.osgi.util.NLS;

import ru.spb.nicetu.altx.mnemo.cell.INodeCell;
import ru.spb.nicetu.altx.mnemo.cell.SettingNodeCell;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.AFunc;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.ASimpleFunc;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.IMemoryCreatingSimpleFunc;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.ISimpleFuncFactory;
import ru.spb.nicetu.altx.mnemo.extpoint.model.funcs.SimpleFuncException;
import ru.spb.nicetu.altx.mnemo.extpoint.plugin.Messages;
import ru.spb.nicetu.altx.mnemo.extpoint.valuetypes.ITemplateValueType;
import ru.spb.nicetu.altx.mnemo.math.managers.SimpleFuncFactoryManager;
import ru.spb.nicetu.altx.mnemo.messages.IRunListener;
import ru.spb.nicetu.altx.mnemo.messages.RunMessage;
import ru.spb.nicetu.altx.mnemo.messages.RunMessage.RunMessageType;

// @SuppressWarnings("serial")
@SuppressWarnings("serial")
public class TaskNode extends BaseNode implements ITaskNode {

  private static final long serialVersionUID = -1847710411531549855L;

  protected String simpleFuncClassName;

  private IRunListener listener = new IRunListener() {

    public void handleRunMessage(RunMessage msg) {
      // System.out.println(TaskNode.this.getConcreteName() +
      // " : " + msg.getMessage());
      if (msg.getType().equals(RunMessageType.ERROR)) {
        fInterrupted = true;
      }
      notifyAllRunListeners(translateMessage(msg));
    }

    private RunMessage translateMessage(RunMessage msg) {
      String str = msg.getMessage();
      String[] cellIDs = msg.getCellIDs();
      List<String> values = new ArrayList<String>();

      Map<String, String> inputCells = new HashMap<String, String>();
      for (INodeCell cell : getAllInputCells()) {
        inputCells.put(cell.getCellID(), cell.getName());
      }
      for (INodeCell cell : getAllSettingCells()) {
        inputCells.put(cell.getCellID(), cell.getName());
      }

      for (String cellID : cellIDs) {
        values.add(inputCells.get(cellID));
      }

      str = NLS.bind(msg.getMessage(), values.toArray());
      return new RunMessage(str, cellIDs, msg.getType());
    }
  };

  public TaskNode(String name, String simpleFuncClassName) {
    super(name);
    this.simpleFuncClassName = simpleFuncClassName;
  }

  @Override
  @SuppressWarnings("unchecked")
  public Object getAdapter(Class adapter) {
    return super.getAdapter(adapter);
  }

  @Override
  @SuppressWarnings("unchecked")
  public void run() {
    setWasRunned(false);

    // Создаем вычислитель, если ещё не создан
    if (simpleFunc == null) {
      ISimpleFuncFactory factory = SimpleFuncFactoryManager.getInstance()
          .getFactory(simpleFuncClassName);
      if (factory == null) {
        notifyAllRunListeners(new RunMessage(this.getConcreteName()+": "+Messages.SimpleFuncNotExist, null,
            RunMessageType.ERROR));
        fInterrupted = true;
        return;
      }
      simpleFunc = factory.createInstance();
    }

    if (simpleFunc == null) {
      notifyAllRunListeners(new RunMessage(Messages.RunInternal, null,
          RunMessageType.ERROR));
      return;
    }

    // Записать значения, поступающие во входные ячейки узла,
    // в ячейки вычислителя
    for (INodeCell cell : getAllInputCells()) {
      // Взять значение из источника (DataNode'a)
      // FIXME TaskNode.run() - проверка на null !!!!
      ICellLink link = getLink(cell);
      INode targetNode = link.getNode();
      INodeCell targetCell = link.getCell();

      // TODO TaskNode.run() прочитать значение из файла или
      // взять из
      // вычислителя
      ITemplateValueType value = targetNode.getCellValue(targetCell);

      simpleFunc.setCellValue(cell.getCellID(), value);
    }
    // Записать значение настроечных параметров в ячейки
    // вичислителя
    for (INodeCell cell : getAllSettingCells()) {
      if (cell instanceof SettingNodeCell) {
        SettingNodeCell settingCell = (SettingNodeCell) cell;

        // TODO (Arkady) генерируем значение из настроечного
        // параметра
        ITemplateValueType value = settingCell.getValue();
        // String value = settingCell.getValue().toString();

        simpleFunc.setCellValue(cell.getCellID(), value);
      }
    }

    // TODO !--??--! TaskNode.run() (Arkady) не должен ли
    // вычислитель бросать
    // Exception если у него не
    // все параметры заданы
    simpleFunc.addRunListener(listener);
    notifyAllRunListeners(new RunMessage(Messages.RunBegins, null,
        RunMessageType.DEBUG));
    try {
      //TODO удалить else при окончательном переходе на AFunc
      if (simpleFunc instanceof AFunc){
        ((AFunc)simpleFunc).safeRun();
      } else {
        simpleFunc.run();
      }
      notifyAllRunListeners(new RunMessage(Messages.RunEnds));
    } catch (Throwable e) {
      if (e instanceof SimpleFuncException) {
        notifyAllRunListeners(new RunMessage(Messages.RunEndsError));
      } else {
        //костыль для функций не AFunc
        RunMessage msg = new RunMessage(Messages.RunUnknown, null, RunMessageType.ERROR);
        if (simpleFunc instanceof ASimpleFunc) {
          ((ASimpleFunc) simpleFunc).notifyAllRunListeners(msg);
        }
        notifyAllRunListeners(msg);
      }
    }

    fWasRunned = true;
    simpleFunc.removeRunListener(listener);
  }

  @Override
  @SuppressWarnings("unchecked")
  public ITemplateValueType getCellValue(INodeCell cell) {
    // ISimpleFuncObjFactory factory = null;

    // XXX (Arkady) TaskNode.getCellValue().
    // Если cell.isInput, то
    // Берем значение из источника данных, т.е. из DataNode
    if (cell.isInput()) {
      ICellLink link2Src = cellLinks.get(cell);
      if (link2Src == null)
        return null;

      INode srcNode = link2Src.getNode();
      if (srcNode == null)
        return null;

      return srcNode.getCellValue(link2Src.getCell());
    }

    // FIXME serg добавлено 05.08.10, надо протестить
    if (cell instanceof SettingNodeCell) {
      ITemplateValueType value = ((SettingNodeCell)cell).getValue();
      return value;
    }

    if (simpleFunc == null) {
      // FIXME
      // fWasRunned = false;
      return null;
    }

    ITemplateValueType value = simpleFunc.getCellValue(cell.getCellID());

    return value;
  }

  @Override
  @SuppressWarnings("unchecked")
  public void setCellValue(INodeCell cell, ITemplateValueType value) {
    // Создаем вычислитель, если ещё не создан
    if (simpleFunc == null) {
      ISimpleFuncFactory factory = SimpleFuncFactoryManager.getInstance()
          .getFactory(simpleFuncClassName);
      if (factory == null)
        return;
      simpleFunc = factory.createInstance();
    }
    if (simpleFunc == null)
      return;
    simpleFunc.setCellValue(cell.getCellID(), value);
  }

  public Collection<IMemoryCellDescriptor> getAllChangedMemoryCells() {
    if (simpleFunc instanceof IMemoryCreatingSimpleFunc) {
      return ((IMemoryCreatingSimpleFunc) simpleFunc)
          .getCreatedMemoryCellDescriprors();
    }
    return new ArrayList<IMemoryCellDescriptor>();
  }
}
